# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
# SPDX-License-Identifier: MIT
import io
import json
import logging
import os
from typing import Annotated, Literal

from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from langchain_core.runnables import RunnableConfig
from langchain_core.tools import tool
from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.types import Command, interrupt

from src.agents import create_agent
from src.config.agents import AGENT_LLM_MAP
from src.config.configuration import Configuration
from src.llms.llm import get_llm_by_type
from src.prompts.planner_model import Plan, StepType
from src.prompts.template import apply_prompt_template
from src.tools import (
    crawl_tool,
    get_web_search_tool,
    get_retriever_tool,
    python_repl_tool,
)
from src.tools.search import LoggedTavilySearch
from src.utils.json_utils import repair_json_output
from .talent_model import Talent
from .types import State
from ..config import SELECTED_SEARCH_ENGINE, SearchEngine
from ..minio.minio_client import upload_file_to_minio
from ..supabase_db.api.ChatMessagesApi import save_simple_tool_calls_message, save_message_chunk, \
    save_tool_calls_message
from ..supabase_db.api.TalentsApi import get_tenants, save_tenant
from ..tools.generote_pdf import gen_pdf_upload

logger = logging.getLogger(__name__)


@tool
def handoff_to_planner(
        task_title: Annotated[str, "The title of the task to be handed off."],
        locale: Annotated[str, "The user's detected language locale (e.g., en-US, zh-CN)."],
):
    """Handoff to planner agent to do plan."""
    # This tool is not returning anything: we're just using it
    # as a way for LLM to signal that it needs to hand off to planner agent
    return


# 未记录message到supabase
def background_investigation_node(
        state: State, config: RunnableConfig
) -> Command[Literal["planner"]]:
    logger.info("background investigation node is running.")
    configurable = Configuration.from_runnable_config(config)
    query = state["messages"][-1].content
    if SELECTED_SEARCH_ENGINE == SearchEngine.TAVILY.value:
        searched_content = LoggedTavilySearch(
            max_results=configurable.max_search_results
        ).invoke(query)
        background_investigation_results = None
        if isinstance(searched_content, list):
            background_investigation_results = [
                {"title": elem["title"], "content": elem["content"]}
                for elem in searched_content
            ]
        else:
            logger.error(
                f"Tavily search returned malformed response: {searched_content}"
            )
    else:
        background_investigation_results = get_web_search_tool(
            configurable.max_search_results
        ).invoke(query)
    return Command(
        update={
            "background_investigation_results": json.dumps(
                background_investigation_results, ensure_ascii=False
            )
        },
        goto="planner",
    )


async def planner_node(
        state: State, config: RunnableConfig
) -> Command[Literal["human_feedback", "reporter"]]:
    """Planner node that generate the full plan."""
    logger.info("Planner generating full plan")
    configurable = Configuration.from_runnable_config(config)
    plan_iterations = state["plan_iterations"] if state.get("plan_iterations", 0) else 0
    messages = apply_prompt_template("planner", state, state["agent_code"], configurable)

    if (
            plan_iterations == 0
            and state.get("enable_background_investigation")
            and state.get("background_investigation_results")
    ):
        messages += [
            {
                "role": "user",
                "content": (
                        "background investigation results of user query:\n"
                        + state["background_investigation_results"]
                        + "\n"
                ),
            }
        ]

    # if AGENT_LLM_MAP["planner"] == "basic":
    #     llm = get_llm_by_type(AGENT_LLM_MAP["planner"]).with_structured_output(
    #         Plan,
    #         method="json_mode",
    #     )
    # else:
    #     llm = get_llm_by_type(AGENT_LLM_MAP["planner"])
    llm = get_llm_by_type(AGENT_LLM_MAP["planner"])

    # if the plan iterations is greater than the max plan iterations, return the reporter node
    if plan_iterations >= configurable.max_plan_iterations:
        return Command(goto="reporter")

    full_response = ""
    id = ""
    if AGENT_LLM_MAP["planner"] == "basic":
        response = await llm.ainvoke(messages)
        # full_response = response.model_dump_json(indent=4, exclude_none=True)
        full_response = response.content
        id = response.id
    else:
        response = await llm.astream(messages)
        id = response.id
        for chunk in response:
            full_response += chunk.content
    logger.debug(f"Current state messages: {state['messages']}")
    logger.info(f"Planner response: {full_response}")

    result = json.loads(repair_json_output(full_response))

    try:
        curr_plan = json.loads(repair_json_output(full_response))
    except json.JSONDecodeError:
        logger.warning("Planner response is not a valid JSON")
        if plan_iterations > 0:
            return Command(goto="reporter")
        else:
            return Command(goto="__end__")
    if curr_plan.get("has_enough_context"):
        logger.info("Planner response has enough context.")
        new_plan = Plan.model_validate(curr_plan)
        # 保存一个chunk_message
        thread_id = config.get("configurable", {}).get("thread_id")
        await save_message_chunk(result, id, state["user_id"], thread_id, "planner")
        return Command(
            update={
                "messages": [AIMessage(content=full_response, name="planner")],
                "current_plan": new_plan,
            },
            goto="reporter",
        )
    # 保存一个chunk_message
    thread_id = config.get("configurable", {}).get("thread_id")
    await save_message_chunk(result, id, state["user_id"], thread_id, "planner")
    return Command(
        update={
            "messages": [AIMessage(content=full_response, name="planner")],
            "current_plan": full_response,
        },
        goto="human_feedback",
    )


async def human_feedback_node(
        state, config: RunnableConfig
) -> Command[Literal["planner", "research_team", "reporter", "__end__"]]:
    current_plan = state.get("current_plan", "")
    # check if the plan is auto accepted
    auto_accepted_plan = state.get("auto_accepted_plan", False)
    if not auto_accepted_plan:

        thread_id = config.get("configurable", {}).get("thread_id")
        # await save_interrupt_message('{}', 'human_feedback:'+str(uuid.uuid4()), state["user_id"], thread_id)
        feedback = interrupt("Please Review the Plan.")

        # if the feedback is not accepted, return the planner node
        if feedback and str(feedback).upper().startswith("[EDIT]"):
            return Command(
                update={
                    "messages": [
                        HumanMessage(content=feedback, name="feedback"),
                    ],
                },
                goto="planner",
            )
        elif feedback and str(feedback).upper().startswith("[ACCEPTED]"):
            logger.info("Plan is accepted by user.")
        else:
            raise TypeError(f"Interrupt value of {feedback} is not supported.")

    # if the plan is accepted, run the following node
    plan_iterations = state["plan_iterations"] if state.get("plan_iterations", 0) else 0
    goto = "research_team"
    try:
        current_plan = repair_json_output(current_plan)
        # increment the plan iterations
        plan_iterations += 1
        # parse the plan
        new_plan = json.loads(current_plan)
        if new_plan["has_enough_context"]:
            goto = "reporter"
    except json.JSONDecodeError:
        logger.warning("Planner response is not a valid JSON")
        if plan_iterations > 0:
            return Command(goto="reporter")
        else:
            return Command(goto="__end__")

    return Command(
        update={
            "current_plan": Plan.model_validate(new_plan),
            "plan_iterations": plan_iterations,
            "locale": new_plan["locale"],
        },
        goto=goto,
    )


async def coordinator_node(
        state: State, config: RunnableConfig
) -> Command[Literal["planner", "background_investigator", "__end__"]]:
    """Coordinator node that communicate with customers."""
    logger.info("Coordinator talking.")
    configurable = Configuration.from_runnable_config(config)
    messages = apply_prompt_template("coordinator", state, state["agent_code"], None)
    response = (
        get_llm_by_type(AGENT_LLM_MAP["coordinator"])
        .bind_tools([handoff_to_planner])
        .invoke(messages)
    )
    logger.debug(f"Current state messages: {state['messages']}")

    goto = "__end__"
    locale = state.get("locale", "en-US")  # Default locale if not specified
    thread_id = config.get("configurable", {}).get("thread_id")

    if len(response.tool_calls) > 0:
        # 保存一条tool_calls消息
        await save_simple_tool_calls_message(response.tool_calls, response.id, state["user_id"], thread_id,
                                             'coordinator')
        goto = "planner"
        if state.get("enable_background_investigation"):
            # if the search_before_planning is True, add the web search tool to the planner agent
            goto = "background_investigator"
        try:
            for tool_call in response.tool_calls:
                if tool_call.get("name", "") != "handoff_to_planner":
                    continue
                if tool_locale := tool_call.get("args", {}).get("locale"):
                    locale = tool_locale
                    break
        except Exception as e:
            logger.error(f"Error processing tool calls: {e}")
    else:
        # 保存一条message_chunk消息
        await save_message_chunk(response.content, response.id, state["user_id"], thread_id, 'coordinator')
        logger.warning(
            "Coordinator response contains no tool calls. Terminating workflow execution."
        )
        logger.debug(f"Coordinator response: {response}")

    return Command(
        update={"locale": locale, "resources": configurable.resources},
        goto=goto,
    )


async def reporter_node(state: State, config: RunnableConfig):
    """Reporter node that write a final report."""
    logger.info("Reporter write final report")
    current_plan = state.get("current_plan")
    input_ = {
        "messages": [
            HumanMessage(
                f"# Research Requirements\n\n## Task\n\n{current_plan.title}\n\n## Description\n\n{current_plan.thought}"
            )
        ],
        "locale": state.get("locale", "en-US"),
    }
    invoke_messages = apply_prompt_template("reporter", input_, state['agent_code'], None)
    observations = state.get("observations", [])

    # Add a reminder about the new report format, citation style, and table usage
    invoke_messages.append(
        HumanMessage(
            content="IMPORTANT: Structure your report according to the format in the prompt. Remember to include:\n\n1. Key Points - A bulleted list of the most important findings\n2. Overview - A brief introduction to the topic\n3. Detailed Analysis - Organized into logical sections\n4. Survey Note (optional) - For more comprehensive reports\n5. Key Citations - List all references at the end\n\nFor citations, DO NOT include inline citations in the text. Instead, place all citations in the 'Key Citations' section at the end using the format: `- [Source Title](URL)`. Include an empty line between each citation for better readability.\n\nPRIORITIZE USING MARKDOWN TABLES for data presentation and comparison. Use tables whenever presenting comparative data, statistics, features, or options. Structure tables with clear headers and aligned columns. Example table format:\n\n| Feature | Description | Pros | Cons |\n|---------|-------------|------|------|\n| Feature 1 | Description 1 | Pros 1 | Cons 1 |\n| Feature 2 | Description 2 | Pros 2 | Cons 2 |",
            name="system",
        )
    )

    for observation in observations:
        invoke_messages.append(
            HumanMessage(
                content=f"Below are some observations for the research task:\n\n{observation}",
                name="observation",
            )
        )
    logger.debug(f"Current invoke messages: {invoke_messages}")
    messages = [msg for msg in state["messages"] if getattr(msg, "name", None) == "report"]
    invoke_messages.extend(messages)

    response = await get_llm_by_type(AGENT_LLM_MAP["reporter"]).ainvoke(invoke_messages)
    response_content = response.content
    logger.info(f"reporter response: {response_content}")
    # 保存一个chunk_message
    thread_id = config.get("configurable", {}).get("thread_id")
    await save_message_chunk(response_content, response.id, state["user_id"], thread_id, "reporter")

    return {"final_report": response_content}


async def reporter_node_ogsm(state: State, config: RunnableConfig):
    """Reporter node that write a final report and generate html"""
    logger.info("Reporter write final report")
    current_plan = state.get("current_plan")
    print(f'current_plan:{current_plan}')
    input_ = {
        "messages": [
            HumanMessage(
                f"# Research Requirements\n\n## Task\n\n{current_plan.title}\n\n## Description\n\n{current_plan.thought}"
            )
        ],
        "locale": state.get("locale", "en-US"),
    }
    invoke_messages = apply_prompt_template("reporter", input_, state['agent_code'], None)
    observations = state.get("observations", [])

    for observation in observations:
        invoke_messages.append(
            HumanMessage(
                content=f"Below are some observations for the research task:\n\n{observation}",
                name="observation",
            )
        )
    logger.debug(f"Current invoke messages: {invoke_messages}")
    messages = [msg for msg in state["messages"] if getattr(msg, "name", None) == "report"]
    invoke_messages.extend(messages)

    response = get_llm_by_type(AGENT_LLM_MAP["reporter"]).invoke(invoke_messages)
    response_content = response.content
    logger.info(f"reporter response: {response_content}")

    # 保存一个chunk_message
    thread_id = config.get("configurable", {}).get("thread_id")
    await save_message_chunk(response_content, response.id, state["user_id"], thread_id, "reporter")

    file_object = io.BytesIO(response_content.encode("utf-8"))  # 以 UTF-8 编码写入内存
    file_object.seek(0)

    thread_id = config.get("configurable", {}).get("thread_id")
    file_name = f'{thread_id}.html'

    file_url = upload_file_to_minio(file_object, file_name, "text/html")

    print(f"file_url:{file_url}")
    return {"final_report": response_content}


async def test_reporter_node(agent_code: str, title: str, thought: str):
    """Reporter node that write a final report."""
    logger.info("Reporter write final report")
    input_ = {
        "messages": [
            HumanMessage(
                f"# Research Requirements\n\n## Task\n\n{title}\n\n## Description\n\n{thought}"
            )
        ],
        "locale": "en-ZH",
    }
    invoke_messages = apply_prompt_template("reporter", input_, agent_code, None)
    observations = []

    # Add a reminder about the new report format, citation style, and table usage
    invoke_messages.append(
        HumanMessage(
            content="IMPORTANT: Structure your report according to the format in the prompt. Remember to include:\n\n1. Key Points - A bulleted list of the most important findings\n2. Overview - A brief introduction to the topic\n3. Detailed Analysis - Organized into logical sections\n4. Survey Note (optional) - For more comprehensive reports\n5. Key Citations - List all references at the end\n\nFor citations, DO NOT include inline citations in the text. Instead, place all citations in the 'Key Citations' section at the end using the format: `- [Source Title](URL)`. Include an empty line between each citation for better readability.\n\nPRIORITIZE USING MARKDOWN TABLES for data presentation and comparison. Use tables whenever presenting comparative data, statistics, features, or options. Structure tables with clear headers and aligned columns. Example table format:\n\n| Feature | Description | Pros | Cons |\n|---------|-------------|------|------|\n| Feature 1 | Description 1 | Pros 1 | Cons 1 |\n| Feature 2 | Description 2 | Pros 2 | Cons 2 |",
            name="system",
        )
    )

    for observation in observations:
        invoke_messages.append(
            HumanMessage(
                content=f"Below are some observations for the research task:\n\n{observation}",
                name="observation",
            )
        )
    logger.debug(f"Current invoke messages: {invoke_messages}")

    response = await get_llm_by_type(AGENT_LLM_MAP["reporter"]).ainvoke(invoke_messages)
    response_content = response.content

    file_object = io.BytesIO(response_content.encode("utf-8"))  # 以 UTF-8 编码写入内存
    file_object.seek(0)

    thread_id = '123456'
    file_name = f'{thread_id}.html'

    file_url = upload_file_to_minio(file_object, file_name, "text/html")
    print(f"file_url:{file_url}")

    return response_content


def research_team_node(
        state: State,
) -> Command[Literal["planner", "researcher", "coder"]]:
    """Research team node that collaborates on tasks."""
    logger.info("Research team is collaborating on tasks.")
    current_plan = state.get("current_plan")
    if not current_plan or not current_plan.steps:
        return Command(goto="planner")
    if all(step.execution_res for step in current_plan.steps):
        return Command(goto="planner")
    for step in current_plan.steps:
        if not step.execution_res:
            break
    if step.step_type and step.step_type == StepType.RESEARCH:
        return Command(goto="researcher")
    if step.step_type and step.step_type == StepType.PROCESSING:
        return Command(goto="coder")
    return Command(goto="planner")


async def _execute_agent_step(
        state: State, config: RunnableConfig, agent, agent_name: str
) -> Command[Literal["research_team"]]:
    """Helper function to execute a step using the specified agent."""
    current_plan = state.get("current_plan")
    observations = state.get("observations", [])

    # Find the first unexecuted step
    current_step = None
    completed_steps = []
    for step in current_plan.steps:
        if not step.execution_res:
            current_step = step
            break
        else:
            completed_steps.append(step)

    if not current_step:
        logger.warning("No unexecuted step found")
        return Command(goto="research_team")

    logger.info(f"Executing step: {current_step.title}, agent: {agent_name}")

    # Format completed steps information
    completed_steps_info = ""
    if completed_steps:
        completed_steps_info = "# Existing Research Findings\n\n"
        for i, step in enumerate(completed_steps):
            completed_steps_info += f"## Existing Finding {i + 1}: {step.title}\n\n"
            completed_steps_info += f"<finding>\n{step.execution_res}\n</finding>\n\n"

    # Prepare the input for the agent with completed steps info
    agent_input = {
        "messages": [
            HumanMessage(
                content=f"{completed_steps_info}# Current Task\n\n## Title\n\n{current_step.title}\n\n## Description\n\n{current_step.description}\n\n## Locale\n\n{state.get('locale', 'en-US')}"
            )
        ]
    }
    # 添加 feedback 消息
    messages = [msg for msg in state["messages"] if getattr(msg, "name", None) is None]
    agent_input["messages"].extend(messages)

    # Add citation reminder for researcher agent
    if agent_name == "researcher":
        if state.get("resources"):
            resources_info = "**The user mentioned the following resource files:**\n\n"
            for resource in state.get("resources"):
                resources_info += f"- {resource.title} ({resource.description})\n"

            agent_input["messages"].append(
                HumanMessage(
                    content=resources_info
                            + "\n\n"
                            + "You MUST use the **local_search_tool** to retrieve the information from the resource files.",
                )
            )

        agent_input["messages"].append(
            HumanMessage(
                content="IMPORTANT: DO NOT include inline citations in the text. Instead, track all sources and include a References section at the end using link reference format. Include an empty line between each citation for better readability. Use this format for each reference:\n- [Source Title](URL)\n\n- [Another Source](URL)",
                name="system",
            )
        )

    # Invoke the agent
    default_recursion_limit = 50
    try:
        env_value_str = os.getenv("AGENT_RECURSION_LIMIT", str(default_recursion_limit))
        parsed_limit = int(env_value_str)

        if parsed_limit > 0:
            recursion_limit = parsed_limit
            logger.info(f"Recursion limit set to: {recursion_limit}")
        else:
            logger.warning(
                f"AGENT_RECURSION_LIMIT value '{env_value_str}' (parsed as {parsed_limit}) is not positive. "
                f"Using default value {default_recursion_limit}."
            )
            recursion_limit = default_recursion_limit
    except ValueError:
        raw_env_value = os.getenv("AGENT_RECURSION_LIMIT")
        logger.warning(
            f"Invalid AGENT_RECURSION_LIMIT value: '{raw_env_value}'. "
            f"Using default value {default_recursion_limit}."
        )
        recursion_limit = default_recursion_limit
    result = await agent.ainvoke(
        input=agent_input, config={"recursion_limit": recursion_limit}
    )
    thread_id = config.get("configurable", {}).get("thread_id")
    # 保存tool_calls消息，过滤出result["messages"]中存在tool_calls的AIMessage消息,根据tool_calls消息的id，从result["messages"]中匹配ToolMessage，如果status并且=‘success'，则根据tool_call_id填充到上面tool_calls的AIMessage的tool_calls数组对应id的result中
    await save_tool_calls_message(result, state["user_id"], thread_id, agent_name)
    # Process the result
    response_content = result["messages"][-1].content
    logger.debug(f"{agent_name.capitalize()} full response: {response_content}")
    # response_content保存一条chunk_message
    await save_message_chunk(response_content, result["messages"][-1].id, state["user_id"], thread_id, agent_name)

    # Update the step with the execution result
    current_step.execution_res = response_content
    logger.info(f"Step '{current_step.title}' execution completed by {agent_name}")

    return Command(
        update={
            "messages": [
                HumanMessage(
                    content=response_content,
                    name=agent_name,
                )
            ],
            "observations": observations + [response_content],
        },
        goto="research_team",
    )


async def _setup_and_execute_agent_step(
        state: State,
        config: RunnableConfig,
        agent_type: str,
        default_tools: list,
) -> Command[Literal["research_team"]]:
    """Helper function to set up an agent with appropriate tools and execute a step.

    This function handles the common logic for both researcher_node and coder_node:
    1. Configures MCP servers and tools based on agent type
    2. Creates an agent with the appropriate tools or uses the default agent
    3. Executes the agent on the current step

    Args:
        state: The current state
        config: The runnable config
        agent_type: The type of agent ("researcher" or "coder")
        default_tools: The default tools to add to the agent

    Returns:
        Command to update state and go to research_team
    """
    configurable = Configuration.from_runnable_config(config)
    mcp_servers = {}
    enabled_tools = {}

    # Extract MCP server configuration for this agent type
    if configurable.mcp_settings:
        for server_name, server_config in configurable.mcp_settings["servers"].items():
            if (
                    server_config["enabled_tools"]
                    and agent_type in server_config["add_to_agents"]
            ):
                mcp_servers[server_name] = {
                    k: v
                    for k, v in server_config.items()
                    if k in ("transport", "command", "args", "url", "env")
                }
                for tool_name in server_config["enabled_tools"]:
                    enabled_tools[tool_name] = server_name

    # Create and execute agent with MCP tools if available
    if mcp_servers:
        async with MultiServerMCPClient(mcp_servers) as client:
            loaded_tools = default_tools[:]
            for tool in client.get_tools():
                if tool.name in enabled_tools:
                    tool.description = (
                        f"Powered by '{enabled_tools[tool.name]}'.\n{tool.description}"
                    )
                    loaded_tools.append(tool)
            agent = create_agent(agent_type, agent_type, loaded_tools, agent_type, state["agent_code"])
            return await _execute_agent_step(state, config, agent, agent_type)
    else:
        # Use default tools if no MCP servers are configured
        agent = create_agent(agent_type, agent_type, default_tools, agent_type, state["agent_code"])
        return await _execute_agent_step(state, config, agent, agent_type)


async def researcher_node(
        state: State, config: RunnableConfig
) -> Command[Literal["research_team"]]:
    """Researcher node that do research"""
    logger.info("Researcher node is researching.")
    configurable = Configuration.from_runnable_config(config)
    tools = [get_web_search_tool(configurable.max_search_results), crawl_tool]
    retriever_tool = get_retriever_tool(state.get("resources", []))
    if retriever_tool:
        tools.insert(0, retriever_tool)
    logger.info(f"Researcher tools: {tools}")
    return await _setup_and_execute_agent_step(
        state,
        config,
        "researcher",
        tools,
    )


async def coder_node(
        state: State, config: RunnableConfig
) -> Command[Literal["research_team"]]:
    """Coder node that do code analysis."""
    logger.info("Coder node is coding.")
    return await _setup_and_execute_agent_step(
        state,
        config,
        "coder",
        [python_repl_tool],
    )


async def reporter_checker_node(
        state: State, config: RunnableConfig
) -> Command[Literal["research_team"]]:
    """Coder node that do code analysis."""
    return Command(
        update={
            "feedback_accept_node": "jd_generator",
            "feedback_edit_node": "reporter"
        },
        goto="human_feedback2"
    )


async def jd_generator_node(
        state: State, config: RunnableConfig
) -> Command[Literal["human_feedback2"]]:
    """简历生成节点."""

    logger.info("JD generator node is running.")

    input_ = {
        "messages": [
        ],
        "locale": state.get("locale", "en-US"),
    }
    invoke_messages = apply_prompt_template("jd_generator", input_, state['agent_code'], None)

    # Add a reminder about the new report format, citation style, and table usage
    # invoke_messages.append(
    #     HumanMessage(
    #         content="IMPORTANT: Structure your report according to the format in the prompt. Remember to include:\n\n1. Key Points - A bulleted list of the most important findings\n2. Overview - A brief introduction to the topic\n3. Detailed Analysis - Organized into logical sections\n4. Survey Note (optional) - For more comprehensive reports\n5. Key Citations - List all references at the end\n\nFor citations, DO NOT include inline citations in the text. Instead, place all citations in the 'Key Citations' section at the end using the format: `- [Source Title](URL)`. Include an empty line between each citation for better readability.\n\nPRIORITIZE USING MARKDOWN TABLES for data presentation and comparison. Use tables whenever presenting comparative data, statistics, features, or options. Structure tables with clear headers and aligned columns. Example table format:\n\n| Feature | Description | Pros | Cons |\n|---------|-------------|------|------|\n| Feature 1 | Description 1 | Pros 1 | Cons 1 |\n| Feature 2 | Description 2 | Pros 2 | Cons 2 |",
    #         name="system",
    #     )
    # )

    invoke_messages.append(
        HumanMessage(
            content=f"Below is the final report:\n\n{state.get("final_report")}",
            name="report",
        )
    )
    # 添加 feedback 消息
    messages = [msg for msg in state["messages"] if getattr(msg, "name", None) == "jd_generator"]
    invoke_messages.extend(messages)

    response = await get_llm_by_type(AGENT_LLM_MAP["reporter"]).ainvoke(invoke_messages)

    response_content = response.content

    logger.info(f"reporter response: {response_content}")
    # 保存一个chunk_message
    thread_id = config.get("configurable", {}).get("thread_id")
    await save_message_chunk(response_content, response.id, state["user_id"], thread_id, "jd_generator")

    return Command(
        update={
            "messages": [AIMessage(content=response_content, name="jd_generator")],
            "jd_content": response_content,
            "feedback_accept_node": "resume_matcher_loop",
            "feedback_edit_node": "jd_generator"
        },
        goto="human_feedback2"
    )


async def test_start_node(
        state: State, config: RunnableConfig
) -> Command[Literal["resume_matcher_loop"]]:
    """简历生成节点."""
    logger.info("test start node is running.")
    jd = '''
            # 高级软件开发工程师（电商方向）

        **工作地点**：北京 / 上海 / 深圳（可选）  
        **所属部门**：京东科技 - 电商技术部

        ---

        ## 职位描述

        我们正在寻找一位经验丰富的软件开发工程师，加入京东科技电商业务技术团队，负责核心交易链路、商品、订单、库存等关键系统的研发与优化工作。你将参与设计并构建高可用、高并发、低延迟的分布式系统，支撑亿级用户的稳定购物体验。

        ---

        ## 岗位职责

        - 负责电商核心系统（如商品、订单、交易、库存等模块）的设计、开发与持续优化；
        - 参与系统架构设计，提升系统稳定性、扩展性与性能；
        - 应对促销活动、秒杀、大促等极端高并发场景下的系统挑战；
        - 推动技术与业务深度融合，提升整体系统效率和用户体验；
        - 与产品、测试、运维、架构师等协同工作，推动项目高质量落地；
        - 持续关注技术前沿，参与技术创新与内部技术分享。

        ---

        ## 任职要求

        - 本科及以上学历，计算机相关专业，5年以上 Java / Golang / C++ 等后端开发经验；
        - 熟悉主流微服务框架（如 Spring Cloud、Dubbo 等），有良好的服务拆分与治理经验；
        - 精通多线程编程、网络编程、JVM 优化、性能调优等，具备扎实的计算机基础；
        - 熟悉 MySQL、Redis、消息队列（如 RocketMQ、Kafka）等中间件的高可用、高性能使用；
        - 有大规模分布式系统、高并发系统设计与落地经验，有电商平台核心模块开发经验者优先；
        - 具备良好的编码习惯与技术文档能力，强烈的责任心、团队合作精神和自我驱动力；
        - 有大型促销活动系统支持经验（如 618、双11）者优先。

        ---

        ## 加分项

        - 有京东、阿里、拼多多、美团、字节等大型互联网电商平台从业经验；
        - 参与过订单引擎、交易撮合、价格引擎等高复杂度模块研发；
        - 熟悉分布式缓存、分布式事务、限流熔断降级、容灾等设计方案；
        - 有全链路压测与容量评估经验。

        ---

        ## 我们提供

        - 面向亿级用户的电商场景，挑战与成长并存；
        - 技术氛围浓厚，鼓励创新与技术驱动；
        - 完善的培训与晋升机制，清晰的职业发展路径；
        - 行业内有竞争力的薪资与福利待遇。
            '''
    resume1 = '''
    # 李磊

**男 | 29岁**  
📞 电话：18190000001  
📧 邮箱：lilei.dev@example.com  
🎯 职位意向：软件开发工程师  
🧭 工作经验：5年以上  
📍 居住地：北京  

---

## 个人简介

具备5年以上一线互联网公司软件开发经验，曾任职于美团，主导并参与多个高并发、高可用系统的设计与开发。熟悉后端架构设计、分布式系统、微服务治理以及DevOps流程，擅长解决复杂系统中的性能瓶颈与稳定性问题。技术栈全面，责任心强，具备良好的团队协作与跨部门沟通能力。

---

## 核心技能

- **编程语言**：Java（熟练）、Go（熟练）、Python（掌握）  
- **后端框架**：Spring Boot / Spring Cloud、gRPC、Dubbo  
- **数据库**：MySQL、Redis、MongoDB、PostgreSQL  
- **中间件**：Kafka、RocketMQ、Zookeeper、Elasticsearch  
- **分布式与微服务**：服务注册与发现、链路追踪、熔断限流、配置中心等  
- **DevOps & 工具链**：Docker、Kubernetes、Jenkins、Git、Prometheus + Grafana  
- **系统能力**：高并发处理、接口性能优化、数据库调优、故障排查能力强  

---

## 工作经历

### 美团 | 软件开发工程师  
*2019年7月 – 2024年6月，北京*  
部门：基础技术平台 / 到店综合业务平台

**主要职责：**

- 负责美团到店业务中多个核心服务系统的开发与维护，日均请求量超千万；
- 主导订单处理模块微服务改造，服务拆分、数据治理与灰度发布策略落地；
- 参与美团自研容器平台上云改造，推动原有服务从物理机迁移至Kubernetes；
- 构建全链路压测方案，提升核心交易链路稳定性，P99延迟降低40%；
- 优化Redis缓存结构及数据库索引设计，接口QPS提升2倍，数据库压力下降30%；
- 推动DevOps自动化工具落地，CI/CD效率提升80%。

**关键项目：**

- ✅ 高并发交易系统优化项目  
- ✅ 微服务架构重构与服务治理平台接入  
- ✅ Kafka + Flink 实时数据处理平台搭建  
- ✅ 接入公司自研APM平台，实现故障自动告警和快速定位  

---

## 教育背景

**重庆邮电大学**  
计算机科学与技术 本科  
2015年9月 – 2019年6月  

---

## 自我评价

- 技术扎实，注重代码质量与工程效率；  
- 对高可用系统建设、性能优化有丰富实战经验；  
- 喜欢研究底层原理，热衷技术分享与团队协作；  
- 有较强的业务理解能力与Owner意识，能独立推进项目落地。  

    '''

    resume2 = '''
    # # 韩梅梅
    # 
    # 📍 上海  
    # 📧 hanmeimei.tech@example.com  
    # 📞 138-0000-0000  
    # 💼 8年Java/Golang开发经验  
    # 
    # ---
    # 
    # ## 个人简介
    # 
    # 8年大型电商系统研发经验，现任阿里巴巴淘宝事业部高级后端工程师，负责交易链路、订单引擎及促销活动系统的核心模块设计与落地。精通高并发分布式系统设计，具备丰富的双11、618等大促系统保障经验。熟悉 Spring Cloud、Dubbo、RocketMQ、Redis 等主流中间件与微服务框架，具备扎实的计算机基础与系统性能优化能力。
    # 
    # ---
    # 
    # ## 工作经历
    # 
    # ### 阿里巴巴集团 - 淘宝事业部  
    # **高级后端开发工程师**  
    # *2019.06 – 至今 | 上海*
    # 
    # **负责模块**：订单引擎、交易服务、价格中心、促销活动平台
    # 
    # - 主导订单链路系统的架构优化，实现多级缓存与异步化拆单策略，日交易处理能力提升40%；
    # - 设计并实现基于 Golang 的促销活动引擎（如满减、阶梯价），支持“双11”亿级流量稳定运行；
    # - 深入参与交易平台中台化改造，推动服务治理升级，落地 Spring Cloud + Nacos + Sentinel 方案；
    # - 优化 Redis 缓存策略与 MQ 解耦机制，降低热点商品下单时Redis QPS 峰值30%以上；
    # - 推动价格系统灰度发布、全链路压测与容量预估，保障系统稳定。
    # 
    # **关键词**：Java、Golang、Spring Cloud、Dubbo、RocketMQ、Redis、JVM调优、Sentinel、Prometheus
    # 
    # ---
    # 
    # ### 腾讯 - 云与智慧产业事业群（CSIG）  
    # **后端开发工程师**  
    # *2016.04 – 2019.05 | 深圳*
    # 
    # - 参与企业级订单与计费系统开发，基于微服务架构设计核心账单聚合逻辑；
    # - 设计高可用缓存策略及MQ异步通知机制，实现账单系统99.99% SLA保障；
    # - 编写覆盖交易流程的单元测试与集成测试，推动 CI/CD 体系搭建。
    # 
    # ---
    # 
    # ## 教育背景
    # 
    # **北京邮电大学**  
    # 计算机科学与技术 本科  
    # *2012.09 – 2016.07*
    # 
    # ---
    # 
    # ## 技术栈
    # 
    # - **编程语言**：Java（主）、Golang（次）、Shell  
    # - **框架中间件**：Spring Boot / Spring Cloud / Dubbo / RocketMQ / Kafka / Redis / MySQL  
    # - **系统设计**：分布式事务、限流熔断降级、幂等机制、容灾设计  
    # - **工具链**：Jenkins、Docker、GitLab、Prometheus、Grafana、Arthas、JProfiler  
    # - **平台经验**：灰度发布、全链路压测（Pandora）、故障演练（ChaosBlade）
    # 
    # ---
    # 
    # ## 项目经历精选
    # 
    # ### 🧩 淘宝价格中心重构项目
    # 
    # - **背景**：原有价格服务模块存在耦合高、响应慢问题；
    # - **方案**：采用DDD分层解耦设计，基于Golang构建轻量化边缘计算节点；
    # - **结果**：系统响应延迟降低50%，大促稳定性提升至99.98%。
    # 
    # ---
    # 
    # ### 🔧 双11秒杀服务限流熔断与容灾设计
    # 
    # - 设计滑动窗口算法限流器，实现自适应限流；
    # - 引入异地多活部署与异步消息补偿机制；
    # - 执行5轮全链路压测与容量评估，支撑10亿级并发请求。
    # 
    # ---
    # 
    # ## 荣誉与认证
    # 
    # - 🏅 阿里巴巴“技术影响力奖” | 2023年Q4  
    # - 🗣 阿里内部“交易技术分享日”讲师  
    # - 📈 腾讯内部T4技术晋升（2018）
    # 
    # ---
    # 
    '''
    match1 = """

    ### 匹配评估报告

    **评估模型：The Specialist/Engineer**
    - **选用理由**: 该职位强调深厚的技术功底和分布式、高并发系统的工程能力，李磊的简历中显示了丰富的相关技术经验和实战案例，符合技术专家型人才画像。
    - **权重分配**: 核心资格匹配度 (50%), 经验相关性与质量 (30%), 过往成就与影响力 (10%), 优先资格匹配度 (10%)

    ---

    **分数明细 (满分10)**
    - **核心资格匹配度**: 9
    - **经验相关性与质量**: 8
    - **过往成就与影响力**: 9
    - **优先资格匹配度**: 7

    ---

    **核心优势分析**
    1.  **扎实的编程语言和技术栈能力**
        - **证据**: "具备5年以上一线互联网公司软件开发经验，熟悉Java（熟练）、Go（熟练）"
    2.  **丰富的微服务与分布式系统经验**
        - **证据**: "后端框架：Spring Boot / Spring Cloud、gRPC、Dubbo"；"服务拆分、数据治理与灰度发布策略落地"
    3.  **高并发与高可用系统设计的实践**
        - **证据**: "负责美团到店业务中多个核心服务系统的开发与维护，日均请求量超千万"；"高并发交易系统优化项目"
    4.  **显著的性能优化和系统稳定性提升**
        - **证据**: "P99延迟降低40%"；"接口QPS提升2倍"；"数据库压力下降30%"

    ---

    **潜在风险与差距**
    1.  **缺少明确电商主站核心模块经验**
        - **证据**: "简历中虽有高并发交易系统经验，但未直接体现电商核心模块如订单引擎、交易撮合等涉及"
    2.  **大型促销活动系统支持经验体现不足**
        - **证据**: "无明确提及618、双11等大型促销活动系统相关经验"

    ---

    **建议面试问题**
    - "请详细介绍您在美团高并发交易系统优化项目中，如何实施微服务架构改造，以及面临的最大技术挑战？"
    - "您如何设计和优化高并发环境下的Redis缓存和数据库架构？能分享具体的性能提升方案吗？"
    - "面对巨大流量波动的促销活动，您有哪些技术策略保障系统稳定和高可用？"
        """
    match_score1 = {'技术团队赋能与知识传承': {'6.1': {'competencies': 7, 'experience': 7, 'knowledge': 8, 'skills': 8},
                                               '6.2': {'competencies': 7, 'experience': 7, 'knowledge': 8,
                                                       'skills': 7}},
                    '技术难点攻关与系统优化': {'3.1': {'competencies': 9, 'experience': 9, 'knowledge': 9, 'skills': 9},
                                               '3.2': {'competencies': 9, 'experience': 8, 'knowledge': 9,
                                                       'skills': 9}},
                    '核心业务驱动与需求分析': {'1.1': {'competencies': 8, 'experience': 9, 'knowledge': 9, 'skills': 9},
                                               '1.2': {'competencies': 8, 'experience': 9, 'knowledge': 8,
                                                       'skills': 9}},
                    '核心研发与代码交付': {'4.1': {'competencies': 9, 'experience': 9, 'knowledge': 9, 'skills': 9},
                                           '4.2': {'competencies': 8, 'experience': 7, 'knowledge': 8, 'skills': 8}},
                    '系统架构设计与平台治理': {'2.1': {'competencies': 8, 'experience': 9, 'knowledge': 9, 'skills': 9},
                                               '2.2': {'competencies': 7, 'experience': 8, 'knowledge': 8,
                                                       'skills': 8}},
                    '运维支持与故障响应': {'5.1': {'competencies': 9, 'experience': 9, 'knowledge': 9, 'skills': 9},
                                           '5.2': {'competencies': 8, 'experience': 8, 'knowledge': 8, 'skills': 9}}}
    match2 = {'技术团队赋能与知识传承': {'6.1': {'competencies': 7, 'experience': 7, 'knowledge': 8, 'skills': 8},
                                         '6.2': {'competencies': 7, 'experience': 7, 'knowledge': 8, 'skills': 7}},
              '技术难点攻关与系统优化': {'3.1': {'competencies': 9, 'experience': 9, 'knowledge': 9, 'skills': 9},
                                         '3.2': {'competencies': 9, 'experience': 8, 'knowledge': 9, 'skills': 9}},
              '核心业务驱动与需求分析': {'1.1': {'competencies': 8, 'experience': 9, 'knowledge': 9, 'skills': 9},
                                         '1.2': {'competencies': 8, 'experience': 9, 'knowledge': 8, 'skills': 9}},
              '核心研发与代码交付': {'4.1': {'competencies': 9, 'experience': 9, 'knowledge': 9, 'skills': 9},
                                     '4.2': {'competencies': 8, 'experience': 7, 'knowledge': 8, 'skills': 8}},
              '系统架构设计与平台治理': {'2.1': {'competencies': 8, 'experience': 9, 'knowledge': 9, 'skills': 9},
                                         '2.2': {'competencies': 7, 'experience': 8, 'knowledge': 8, 'skills': 8}},
              '运维支持与故障响应': {'5.1': {'competencies': 9, 'experience': 9, 'knowledge': 9, 'skills': 9},
                                     '5.2': {'competencies': 8, 'experience': 8, 'knowledge': 8, 'skills': 9}}}

    question1 = """
    # 京东高级软件开发工程师（电商方向）面试指导方案

### 审核背景
> *目标：评估李磊在电商核心系统（订单、商品、库存）方面的能力与经验，辨别其是否适配京东电商板块的高性能、可扩展系统需求。* 
> *候选人资料：具备5年以上互联网公司开发经验，擅长分布式、高并发系统设计，曾在美团主导高并发订单系统优化，技术全面，责任心强。* 

---

### 初步筛选面试
> *焦点：验证基础技术能力、理解深度及文化匹配。

---

**1. [高优先级 | 证据深度挖掘 - EDD]**

> *请介绍你在美团订单处理模块微服务重构中的具体职责及技术细节，特别是如何解决高并发环境下的性能瓶颈？*

- **Rationale:** 探索候选人在核心订单系统的实战能力，判断其实际技术深度和解决问题的能力。
- **评价标准:** 具体描述技术方案与优化措施，如缓存策略、数据库调优、微服务拆分经验，以及实际效果（延迟削减、QPS提升等）。
- **潜在追问:**
  - "在实施过程中，遇到的最大挑战和应对措施是什么？"
  - "如果没有具体项目经验，你会如何设计一个能支持高并发的订单系统？"

---

**2. [中等优先 | 价值观与文化匹配 - CVA]**

> *我们重视团队合作和持续创新，能否分享一次你与团队合作解决复杂技术难题的经历？并说明你在其中扮演的角色？*

- **Rationale:** 评估其团队合作精神、主动性及对公司文化的适应度。
- **评价标准:** 具体例子、合作中的沟通与协调能力、学习与创新意愿。
- **潜在追问:**
  - "在合作中出现分歧时，你通常如何处理？"
  - "你如何推动创新在团队中的落地？"

---

**3. [低优先 | 能力验证 - Evidence Deep Dive]**

> *你在简历中提到过优化Redis缓存结构，有没有具体的场景和成效可以分享？能详细讲讲你采取的措施吗？*

- **Rationale:** 验证其在高性能缓存优化方面的实际经验。
- **评价标准:** 针对实际场景的理解、策略设计、效果体现（如QPS提升、延迟下降等）。
- **潜在追问:**
  - "如果缓存命中率低，你会采取哪些措施？"
  - "在高并发场景下，如何保证缓存的一致性？"

---

### 技术能力深挖
> *重点：深入理解候选人对分布式系统、微服务架构、性能优化等方面的掌握程度，确保其技术基础符合岗位要求。*

---

**4. [高优先级 | 关键挑战场景模拟 - CBS]**

> *假设京东双11促销期间系统压力激增，你会如何设计和调整系统架构以保障稳定和高效？请详细描述你的应对策略。*

- **Rationale:** 预测候选人在极端压力环境下的系统应变能力与预案设计。
- **评价标准:** 系统扩展方案、容灾备份、限流策略、监控预警等具体措施，以及多系统协调能力。
- **潜在追问:**
  - "你如何进行容量评估和容量规划？"
  - "遇到突发故障时，你会采取哪些应急措施？"

---

**5. [中等优先 | 技术细节验证 - EDD]**

> *请描述你在美团订单系统中，如何实现事务的分布式管理，确保数据一致性？有哪些难点并是怎样解决的？*

- **Rationale:** 了解候选人对分布式事务和数据一致性的掌握水平。
- **评价标准:** 使用的技术方案（如XA、TCC等）、难点处理措施及实践成效。
- **潜在追问:**
  - "在实际操作中遇到的最大难题是什么？"
  - "对微服务架构中分布式事务的看法？"

---

### 结语
> *整体考量：候选人是否具备胜任京东电商核心系统开发的技术深度、现场解决问题能力，以及与公司文化的融合潜力。*

---

**总结备注**
- 密切关注候选人在高并发、高可用、系统优化方面的实战经验和思路。"
- 评估其是否有针对电商场景特别的系统设计经验或能力缺口。"






# 京东电商高级软件开发工程师面试指南

### 一级标题

# 面试整体规划

### Screening 初筛面试
> *Focus: 初步评估候选人是否符合岗位的核心技能与经验，确认其对电商系统的熟悉程度与适配性。*

---

**1. [High Priority | CBS]**

> *请您详细描述一下在阿里巴巴负责的订单链路架构优化中，遇到的最具挑战性的技术难题，以及您是如何解决的？*

- **Rationale:** 了解候选人应对复杂系统架构优化的能力及解决问题的思路。
- **Evaluation Criteria (What to listen for):**
  - 针对具体技术难点的清晰描述
  - 创新或优化方案的合理性
  - 解决方案的实用性和效果
- **Potential Follow-ups:**
  - “在实施过程中，是否遇到新的瓶颈？你是如何应对的？”
  - “这次优化带来了哪些具体的性能提升？”

---

**2. [High Priority | CVA]**

> *京东强调高效协作与创新精神，您在团队合作中遇到意见分歧时，通常如何处理？能举个例子吗？*

- **Rationale:** 评估候选人在团队合作中表达与执行能力及价值观的契合度。
- **Evaluation Criteria:**
  - 有效沟通和协调能力
  - 解决冲突的策略
  - 以结果和团队利益为导向
- **Potential Follow-ups:**
  - “在遇到关键技术意见不合时，您会采取什么方式达成一致？”
  - “有无带领团队突破难题的经历？”

---

**3. [Medium Priority | Evidence Deep Dive]**

> *您在阿里巴巴实现支持“亿级流量”的促销活动系统时，采用了哪些关键技术？可以具体说说其中的技术难点和应对措施吗？*

- **Rationale:** 核实候选人在高并发高流量场景下的实际操作经验。 
- **Evaluation Criteria:**
  - 技术方案的合理性与创新点
  - 具体技术措施（如限流、异步处理等）
  - 性能保障的效果
- **Potential Follow-ups:**
  - “系统稳定性方面，遇到过哪些突发情况？是如何应对的？”
  - “未来还想在这个方向做哪些优化？”

---

### Technical 技术能力面试

> *Focus: 深入考察候选人在分布式架构、高并发和系统优化方面的实战能力。*

---

**4. [High Priority | CBS]**

> *在设计支持亿级流量的秒杀系统时，你采用了哪些技术架构？如何保证系统在高峰时段的稳定？*

- **Rationale:** 探索候选人应对极端流量的架构设计能力。
- **Evaluation Criteria:**
  - 高并发架构策略（如限流、弹性伸缩）
  - 灾备与容灾措施
  - 系统监控与自动化调优
- **Potential Follow-ups:**
  - “遇到过哪些实际故障？是如何快速响应的？”
  - “未来有什么技术打算进一步提升系统性能？”

---

**5. [High Priority | Evidence Deep Dive]**

> *请详细介绍一次你在阿里巴巴进行性能优化的具体实例，比如JVM调优或系统瓶颈突破，效果如何？*

- **Rationale:** 验证候选人在性能调优方面的具体实践经验。
- **Evaluation Criteria:**
  - 分析问题的深度
  - 针对性强的优化措施
  - 性能提升效果的可量化指标
- **Potential Follow-ups:**
  - “优化过程中遇到最大困难是什么？你是如何解决的？”
  - “还有哪些方面希望继续优化？”

---

**6. [Medium Priority | CVA]**

> *京东注重技术创新和快速响应变化，请问你是如何保持技术敏锐度的？在工作中怎么应用新技术？*

- **Rationale:** 了解候选人的学习能力和技术热情。
- **Evaluation Criteria:**
  - 持续学习习惯
  - 实际应用新技术的案例
  - 对未来技术趋势的洞察
  - **Potential Follow-ups:**
    - “你最近关注的技术热点是什么？”
    - “在实际工作中，第一时间尝试新技术的例子？”

---

### Final 战略性面试

> *Focus: 综合评估候选人的战略视野、业务理解和未来发展潜力，判断其是否适配京东的长远战略。*

---

**7. [High Priority | CBS]**

> *京东在大促期间对系统的稳定性和客户体验有极高的要求，假如你负责的核心系统在促销期间突然出现性能瓶颈，你会怎么诊断和应对？*

- **Rationale:** 测试候选人在突发事件管理和系统大规模应变能力。
- **Evaluation Criteria:**
  - 监控与诊断工具的使用
  - 优先级排序与应急措施
  - 沟通协调能力
- **Potential Follow-ups:**
  - “有没有过类似的应急处理经验？结果如何？”
  - “事后你会采取哪些措施防止类似问题再次发生？”

---

**8. [High Priority | CVA]**

> *你怎么看待高速成长的团队文化？你过去是如何推动团队技术分享与持续学习的？*

- **Rationale:** 评估候选人的团队领导力和文化融入能力。
- **Evaluation Criteria:**
  - 团队合作与领导能力
  - 推动技术成长的具体措施
  - 对创新和持续学习的态度
- **Potential Follow-ups:**
  - “在带领团队时，如何平衡个人成长与团队目标？”
  - “遇到团队成员技术瓶颈，你的应对策略？”

---

**9. [Medium Priority | Evidence Deep Dive]**

> *请描述一次你带领团队成功上线的复杂项目，特别是在高压环境下保证质量的经验。*

- **Rationale:** 了解候选人项目管理与团队协作能力。
- **Evaluation Criteria:**
  - 项目管理技巧
  - 风险控制方案
  - 团队合作效果
  - **Potential Follow-ups:**
    - “项目中遇到的最大挑战是什么？”
    - “你是如何协调团队资源应对的？”

---

*注：请保持面试的问题紧扣候选人简历表现与岗位需求，确保覆盖技术能力、项目经验、团队合作以及文化适配等关键维度。将在面试中灵活结合提问，深入挖掘候选人的潜力。*

    """
    question2 = """
    # 京东电商高级软件开发工程师面试指南

### 一级标题

# 面试整体规划

### Screening 初筛面试
> *Focus: 初步评估候选人是否符合岗位的核心技能与经验，确认其对电商系统的熟悉程度与适配性。*

---

**1. [High Priority | CBS]**

> *请您详细描述一下在阿里巴巴负责的订单链路架构优化中，遇到的最具挑战性的技术难题，以及您是如何解决的？*

- **Rationale:** 了解候选人应对复杂系统架构优化的能力及解决问题的思路。
- **Evaluation Criteria (What to listen for):**
  - 针对具体技术难点的清晰描述
  - 创新或优化方案的合理性
  - 解决方案的实用性和效果
- **Potential Follow-ups:**
  - “在实施过程中，是否遇到新的瓶颈？你是如何应对的？”
  - “这次优化带来了哪些具体的性能提升？”

---

**2. [High Priority | CVA]**

> *京东强调高效协作与创新精神，您在团队合作中遇到意见分歧时，通常如何处理？能举个例子吗？*

- **Rationale:** 评估候选人在团队合作中表达与执行能力及价值观的契合度。
- **Evaluation Criteria:**
  - 有效沟通和协调能力
  - 解决冲突的策略
  - 以结果和团队利益为导向
- **Potential Follow-ups:**
  - “在遇到关键技术意见不合时，您会采取什么方式达成一致？”
  - “有无带领团队突破难题的经历？”

---

**3. [Medium Priority | Evidence Deep Dive]**

> *您在阿里巴巴实现支持“亿级流量”的促销活动系统时，采用了哪些关键技术？可以具体说说其中的技术难点和应对措施吗？*

- **Rationale:** 核实候选人在高并发高流量场景下的实际操作经验。 
- **Evaluation Criteria:**
  - 技术方案的合理性与创新点
  - 具体技术措施（如限流、异步处理等）
  - 性能保障的效果
- **Potential Follow-ups:**
  - “系统稳定性方面，遇到过哪些突发情况？是如何应对的？”
  - “未来还想在这个方向做哪些优化？”

---

### Technical 技术能力面试

> *Focus: 深入考察候选人在分布式架构、高并发和系统优化方面的实战能力。*

---

**4. [High Priority | CBS]**

> *在设计支持亿级流量的秒杀系统时，你采用了哪些技术架构？如何保证系统在高峰时段的稳定？*

- **Rationale:** 探索候选人应对极端流量的架构设计能力。
- **Evaluation Criteria:**
  - 高并发架构策略（如限流、弹性伸缩）
  - 灾备与容灾措施
  - 系统监控与自动化调优
- **Potential Follow-ups:**
  - “遇到过哪些实际故障？是如何快速响应的？”
  - “未来有什么技术打算进一步提升系统性能？”

---

**5. [High Priority | Evidence Deep Dive]**

> *请详细介绍一次你在阿里巴巴进行性能优化的具体实例，比如JVM调优或系统瓶颈突破，效果如何？*

- **Rationale:** 验证候选人在性能调优方面的具体实践经验。
- **Evaluation Criteria:**
  - 分析问题的深度
  - 针对性强的优化措施
  - 性能提升效果的可量化指标
- **Potential Follow-ups:**
  - “优化过程中遇到最大困难是什么？你是如何解决的？”
  - “还有哪些方面希望继续优化？”

---

**6. [Medium Priority | CVA]**

> *京东注重技术创新和快速响应变化，请问你是如何保持技术敏锐度的？在工作中怎么应用新技术？*

- **Rationale:** 了解候选人的学习能力和技术热情。
- **Evaluation Criteria:**
  - 持续学习习惯
  - 实际应用新技术的案例
  - 对未来技术趋势的洞察
  - **Potential Follow-ups:**
    - “你最近关注的技术热点是什么？”
    - “在实际工作中，第一时间尝试新技术的例子？”

---

### Final 战略性面试

> *Focus: 综合评估候选人的战略视野、业务理解和未来发展潜力，判断其是否适配京东的长远战略。*

---

**7. [High Priority | CBS]**

> *京东在大促期间对系统的稳定性和客户体验有极高的要求，假如你负责的核心系统在促销期间突然出现性能瓶颈，你会怎么诊断和应对？*

- **Rationale:** 测试候选人在突发事件管理和系统大规模应变能力。
- **Evaluation Criteria:**
  - 监控与诊断工具的使用
  - 优先级排序与应急措施
  - 沟通协调能力
- **Potential Follow-ups:**
  - “有没有过类似的应急处理经验？结果如何？”
  - “事后你会采取哪些措施防止类似问题再次发生？”

---

**8. [High Priority | CVA]**

> *你怎么看待高速成长的团队文化？你过去是如何推动团队技术分享与持续学习的？*

- **Rationale:** 评估候选人的团队领导力和文化融入能力。
- **Evaluation Criteria:**
  - 团队合作与领导能力
  - 推动技术成长的具体措施
  - 对创新和持续学习的态度
- **Potential Follow-ups:**
  - “在带领团队时，如何平衡个人成长与团队目标？”
  - “遇到团队成员技术瓶颈，你的应对策略？”

---

**9. [Medium Priority | Evidence Deep Dive]**

> *请描述一次你带领团队成功上线的复杂项目，特别是在高压环境下保证质量的经验。*

- **Rationale:** 了解候选人项目管理与团队协作能力。
- **Evaluation Criteria:**
  - 项目管理技巧
  - 风险控制方案
  - 团队合作效果
  - **Potential Follow-ups:**
    - “项目中遇到的最大挑战是什么？”
    - “你是如何协调团队资源应对的？”

---

*注：请保持面试的问题紧扣候选人简历表现与岗位需求，确保覆盖技术能力、项目经验、团队合作以及文化适配等关键维度。将在面试中灵活结合提问，深入挖掘候选人的潜力。*

    """

    record1 = """
    # 京东电商高级软件开发工程师面试记录

- **候选人姓名**：李雷
- **面试官**：王主管
- **面试时间**：2025-07-15  
- **岗位名称**：高级软件开发工程师  
- **面试轮次**：第一轮（Screening 初筛面）

---

## 面试环节一：Screening 初筛面试

### 问题 1：[High Priority | CBS]

**面试官：**  
请您详细描述一下在阿里巴巴负责的订单链路架构优化中，遇到的最具挑战性的技术难题，以及您是如何解决的？

**候选人：**  
在阿里巴订单链路中，最大的问题是链路太长、服务过多，导致在高并发促销场景下容易出现瓶颈。我遇到的主要挑战是某个微服务调用链条中存在级联超时。为了解决这个问题，我主导了链路收敛优化，使用了服务编排+异步消息化改造关键路径，并引入了自研链路追踪工具做实时指标打点。最终使整体下单成功率从99.2%提升到99.8%，平均响应时间下降了20%。

**面试官追问：**  
在实施过程中是否遇到新的瓶颈？你是如何应对的？

**候选人：**  
在初期上线时，消息队列出现积压，我通过引入动态消费者线程池自动扩缩容机制，结合RocketMQ延迟消息机制，保证了异步链路的稳定性。

---

### 问题 2：[High Priority | CVA]

**面试官：**  
京东强调高效协作与创新精神，您在团队合作中遇到意见分歧时，通常如何处理？能举个例子吗？

**候选人：**  
我曾在“多仓库库存同步”项目中，与另一个架构师就“先写后同步”还是“分布式强一致”策略有分歧。我组织了一次技术讨论会，列出了不同方案在吞吐量、一致性和可维护性上的权衡，最后团队投票采用了“可配置的策略切换”方案。在项目推进中，我协助对方梳理了落地细节，项目如期完成，性能和一致性均达标。

---

### 问题 3：[Medium Priority | Evidence Deep Dive]

**面试官：**  
您在阿里巴巴实现支持“亿级流量”的促销活动系统时，采用了哪些关键技术？可以具体说说其中的技术难点和应对措施吗？

**候选人：**  
关键技术包括多级缓存（本地 + Redis）、动态配置限流、异步削峰（Kafka + 限流队列）和热点数据预加载。技术难点是用户行为极度集中，秒杀入口每秒超过百万请求。我们利用“滑动窗口限流 + 预热策略”将QPS打散，还构建了“秒杀验证码 +异步库存预占”机制防止抢单脚本攻击。

---

## 面试环节二：Technical 技术能力面试

### 问题 4：[High Priority | CBS]

**面试官：**  
在设计支持亿级流量的秒杀系统时，你采用了哪些技术架构？如何保证系统在高峰时段的稳定？

**候选人：**  
整体架构是前端限流 + 接入层灰度熔断 + 服务层读写分离 + 异步削峰 + 内部队列异步写库。前端通过滑动窗口限流做首道防线，服务层采用多机房容灾架构，基于Sentinel做热点流量隔离。高峰期自动扩容，Prometheus + Grafana 实现全链路可视化监控。大促期间最大支撑峰值达 QPS 500W。

---

### 问题 5：[High Priority | Evidence Deep Dive]

**面试官：**  
请详细介绍一次你在阿里巴巴进行性能优化的具体实例，比如 JVM 调优或系统瓶颈突破，效果如何？

**候选人：**  
在一个广告推荐系统中，发现JVM GC频繁影响RT，通过对内存模型和对象生命周期分析，我将原本频繁创建的短生命周期对象优化为对象池复用，同时调整 G1 GC 参数，将堆内分区比例调整为8:2:2（Eden:Survivor:Old）。GC次数下降 40%，系统吞吐量提升 25%。

---

## 面试环节三：Final 战略性面试

### 问题 7：[High Priority | CBS]

**面试官：**  
京东在大促期间对系统的稳定性和客户体验有极高的要求，假如你负责的核心系统在促销期间突然出现性能瓶颈，你会怎么诊断和应对？

**候选人：**  
第一步用Prometheus+Grafana查看各链路指标，定位是订单数据库TPS接近极限。我立即切换为只写缓存，异步落库的应急模式。同时调高线程池队列容量并开启降级策略，保障核心下单路径不中断。10分钟内恢复服务。事后通过SQL慢查询分析优化索引结构，并对热点数据表做分区分库处理。

---

### 问题 8：[High Priority | CVA]

**面试官：**  
你怎么看待高速成长的团队文化？你过去是如何推动团队技术分享与持续学习的？

**候选人：**  
我认为成长型文化必须以“反馈机制”+“容错环境”为基础。在团队中，我每月组织1次技术主题分享，推动“走出去讲技术、请进来评代码”的文化。我还搭建了内部“技术雷达”平台，让大家提交探索新技术的实践报告，用积分激励机制鼓励分享。我个人也每季度带一个新人成长项目，输出技术成长路径。

---

### 问题 9：[Medium Priority | Evidence Deep Dive]

**面试官：**  
请描述一次你带领团队成功上线的复杂项目，特别是在高压环境下保证质量的经验。

**候选人：**  
在2024年双11，我主导了“预售订单链路重构”项目，全链路涉及8个服务、3个中间件系统。在仅有6周的时间里，我采用灰度发布策略，配合自动化压测工具，每个环节都设立了监控预警机制。上线当天无告警，服务稳定运行，处理订单超1.5亿单。事后我组织复盘，沉淀了《复杂订单系统发布手册》。

---

# 面试总结

- **技术能力评估**：5
- **项目经验评估**：5 
- **团队协作与领导力**：4
- **文化契合度**：5 

---
    """
    record2 = """
    # 京东电商高级软件开发工程师 — 面试记录

- **候选人姓名：** 韩梅梅  
- **面试岗位：** 电商方向高级软件开发工程师  
- **面试官：** 李雷  
- **面试时间：** 2025年7月15日  
- **面试轮次：** Screening 初筛面试  
- **面试时长：** 45分钟  

---

## 1. 技术背景与项目经验探查

**问题：** 请您详细描述一下在阿里巴巴负责的订单链路架构优化中，遇到的最具挑战性的技术难题，以及您是如何解决的？

**候选人回答概要：**  
韩梅梅在阿里巴巴主导订单链路架构优化，将原有“单体服务 + MQ”架构升级为基于 Spring Cloud 的微服务架构。主要难点是跨服务事务一致性问题，尤其在订单创建与支付解耦后出现了消息丢失和幂等控制困难。

她采用“本地消息表 + 补偿机制”的方案来确保可靠投递，并引入 RocketMQ 事务消息实现异步解耦。配合 Prometheus + Grafana 进行链路监控和瓶颈排查，最终将故障率降低 70%，TPS 提升 20%。

**面试官评价：**  
- ✅ 技术难点识别准确，拆解清晰  
- ✅ 优化方案合理且具备实战落地性  
- ✅ 表达逻辑严谨，有架构视角  

**评分：** ★★★★☆（4.5/5）

---

## 2. 团队协作与文化适配

**问题：** 京东强调高效协作与创新精神，您在团队合作中遇到意见分歧时，通常如何处理？能举个例子吗？

**候选人回答概要：**  
她回忆起一次双11活动压测设计中，团队在是否使用客户端限流或服务端限流存在分歧。她组织专题讨论并推动进行小规模 A/B 测试，用数据结果做决策，最终采用了她主导优化的异步服务端限流方案（基于漏桶模型），方案效果优秀并广泛推广。

**面试官评价：**  
- ✅ 沟通方式积极，重视数据驱动  
- ✅ 具备推动落地能力与团队影响力  
- ✅ 合作态度开放，能达成共识  

**评分：** ★★★★★（5/5）

---

## 3. 高并发系统实战能力

**问题：** 您在阿里实现支持“亿级流量”的促销活动系统时，采用了哪些关键技术？可以具体说说其中的技术难点和应对措施吗？

**候选人回答概要：**  
她介绍活动系统采用了前端预热+后端削峰架构：
- Redis 预加载库存；
- Lua 脚本实现原子扣减；
- Kafka 做异步下单流转；
- Sentinel 控制限流策略；
- 使用 Canal + ES 实现实时活动数据回流。

遇到的核心问题是 Redis 热点 Key 的并发争抢及库存超卖问题。她使用分布式锁、合理 key 设计与冷静重试策略来应对。

**面试官评价：**  
- ✅ 具备应对亿级流量系统的实战经验  
- ✅ 技术栈覆盖全面，措施合理  
- ✅ 对性能瓶颈有敏感度和控制能力  

**评分：** ★★★★☆（4.5/5）

---

## 4. 技术敏锐度与创新意识

**问题：** 请问你是如何保持技术敏锐度的？在工作中怎么应用新技术？

**候选人回答概要：**  
韩梅梅定期设定学习主题，如 Serverless、eBPF、Rust。她提到在一次活动系统中，引入阿里函数计算 FC 完成异步风控处理，将资源利用率提升 40%。她还在团队内部组织“技术雷达”讨论会，引导团队学习和复盘技术趋势。

**面试官评价：**  
- ✅ 有明确的学习规划和方向  
- ✅ 具备新技术快速应用的能力  
- ✅ 传导和分享意识强  

**评分：** ★★★★★（5/5）

---

## 总结评价：

韩梅梅是一位综合素质非常强的候选人：

- ✅ 技术能力扎实，具有大规模高并发系统的设计与优化经验；
- ✅ 沟通能力出色，能在复杂环境中推动团队协作与共识；
- ✅ 热爱技术，有明确的自驱学习目标，并能在实际工作中落地；
- ✅ 与京东工程文化高度契合，具备未来成长为技术负责人或架构师的潜力。
---

    """

    final_report = """
    [资深后端工程师]综合能力分析矩阵

| 岗位名称 | 职责类别 | 岗位责任 | 关键任务 | 关键任务挑战 | 经验 | 知识 | 技能 | 胜任力 |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| 资深后端工程师 | 核心业务驱动与需求分析 | 深入理解业务需求，参与并推动核心系统平台与新业务的设计与实现，将技术方案与商业目标有效融合 | 1.1 分析业务需求并提炼技术解决方案<br>1.2 推动与产品、前端、运维等团队的跨部门协作 | 1.1 需求多变且复杂，难以准确定位技术落地路径<br>1.2 团队沟通壁垒导致需求协作效率低 | 1.1 3年以上大中型系统需求分析与业务落地经验<br>1.2 具备跨部门项目协作经验 | 1.1 精通面向对象设计、敏捷需求分析方法<br>1.2 掌握互联网主流业务流程 | 1.1 业务建模与需求澄清能力<br>1.2 高效沟通与会议协调能力 | 1.1 高业务敏锐度、系统性思考<br>1.2 主动沟通、善于驱动协作 |
| | 系统架构设计与平台治理 | 主导系统架构设计、核心技术方案制定及高可用、高性能、高扩展性平台建设，实现平台治理与架构演进 | 2.1 设计与实施高并发分布式系统架构<br>2.2 制定并推行平台统一标准、架构规范 | 2.1 层级复杂、流量极大、性能与可用性压力大<br>2.2 协同大量业务线，标准与规范难执行落地 | 2.1 主导大型分布式系统架构设计经验<br>2.2 参与或推动平台统一治理项目经验 | 2.1 掌握分布式、微服务、云原生架构核心原理<br>2.2 熟悉中间件、DevOps、平台安全治理体系 | 2.1 架构设计与高并发调优能力<br>2.2 技术规范文档撰写与推广能力 | 2.1 技术前瞻、追求高标准<br>2.2 推动变革、影响力强 |
| | 技术难点攻关与系统优化 | 持续优化系统性能、稳定性与可靠性，解决高并发、高可用及数据一致性等核心技术难题，提升全链路能力 | 3.1 高效定位并解决系统瓶颈、疑难故障<br>3.2 进行性能压测与关键资源优化 | 3.1 复杂性高、Bug难复现、需跨系统联调<br>3.2 性能与成本难以兼顾，优化空间有限 | 3.1 多年线上疑难问题排查与核心障碍突破经验<br>3.2 大流量系统性能优化及容量规划经验 | 3.1 掌握常用调优手段、分布式一致性协议、可观测性理论<br>3.2 性能测试方法、接口瓶颈分析方法 | 3.1 故障排查分析能力<br>3.2 性能压测与代码/存储/网络联合优化能力 | 3.1 冷静应对、抗压性强<br>3.2 善于持续追踪分析细节 |
| | 核心研发与代码交付 | 主导核心功能模块研发，实现高质量、易维护的代码产出，确保按时高效交付业务需求 | 4.1 研发核心业务模块、接口及中间件<br>4.2 代码Review并推动工程质量提升 | 4.1 需求繁杂、交付周期紧张、技术债高<br>4.2 团队成员水平参差，代码一致性难控 | 4.1 主导或独立开发关键功能/中间件模块<br>4.2 成功带领团队进行代码评审和重构 | 4.1 精通主流后端编程语言（Java、Go等）及主流开发框架<br>4.2 熟悉CI/CD、单元测试、代码规范体系 | 4.1 高效编码与重构能力<br>4.2 代码Review与质量改进能力 | 4.1 注重细节、结果导向<br>4.2 工程意识强、严谨自律 |
| | 运维支持与故障响应 | 主动参与运维支持，负责线上问题快速响应、故障恢复及自动化监控体系完善，保障核心业务持续可用 | 5.1 搭建与维护自动化监控与报警体系<br>5.2 及时响应并处理线上突发故障 | 5.1 监控体系覆盖难、报警误报漏报多<br>5.2 故障定位与恢复需极强时效性与容错 | 5.1 构建/维护监控体系或相关自动化经验<br>5.2 一线大型系统事故处置和应急恢复经验 | 5.1 熟悉主流APM、日志系统、可观测性工具<br>5.2 熟悉应急预案与故障应对策略 | 5.1 监控报警策略制定与优化能力<br>5.2 快速排查与应急处理能力 | 5.1 主动负责、风险意识强<br>5.2 保持冷静、极强执行力 |
| | 技术团队赋能与知识传承 | 担任团队技术骨干，通过分享、带教、技术推广等方式提升团队整体技术能力与氛围 | 6.1 指导初中级工程师成长，推动技术分享<br>6.2 参与团队技术文化建设与标准制定 | 6.1 人员层次多元，带教难以精准赋能<br>6.2 团队协作与知识沉淀机制弱 | 6.1 带教/mentor初中级工程师的实际经验<br>6.2 参与或组织团队技术建设项目经验 | 6.1 教练型领导力、知识管理方法论<br>6.2 行业内最佳实践及分享技巧 | 6.1 技术讲解与疑难辅导能力<br>6.2 组织推动与团队文化塑造能力 | 6.1 主动助人、乐于分享<br>6.2 团队协作意识、组织影响力 |
    """
    interview_evaluation_content = """
    # 决策备忘录与面试综合分析(Decision Memo & Interview Synthesis): 李磊 (Li Lei)\n\n## 1. 决策者仪表盘(Decision-Maker's Dashboard)\n\n### 能力证据矩阵 (Competency Evidence Matrix) (基于面试表现 (Based on Interview Performance))\n\n| 核心能力(Core Competency)    | 基于证据的评级(Evidence-Based Rating) | 分数(Score) (1-10) | 关键支撑引语(Key Supporting Quote)                                                                                                                                       |\n| :---------------------- | :----------------------------- | :------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Strategic Thinking**  | `Exceeds Expectations`         | 9              | > \"在促销期间数据库TPS接近极限时，我立即切换为只写缓存、异步落库的应急模式，并通过SQL慢查询分析优化索引结构，对热点数据表做分区分库处理。\"                                |\n| **Team Leadership**     | `Strong Performance`           | 8              | > \"我组织了一次技术讨论会，列出了不同方案在吞吐量、一致性和可维护性上的权衡，最后团队投票采用了‘可配置的策略切换’方案。在项目推进中，我协助对方梳理了落地细节。\"          |\n| **Technical Execution** | `Outstanding`                  | 10             | > \"GC次数下降40%，系统吞吐量提升25%。在广告推荐系统中，通过对象池及GC参数优化彻底改善了性能瓶颈。双11订单链路重构项目高并发处理订单超1.5亿单，无告警。\"       |\n| **Cultural Alignment**  | `Exceeds Expectations`         | 9              | > \"我每月组织技术主题分享，搭建了‘技术雷达’平台，推行积分激励，季度带新人成长项目，强调反馈与容错文化。\"\n\n---\n\n## 2. 整体面试印象(Holistic Interview Impression) (阶段一分析 (First-Pass Analysis))\n\n> 李磊表现出色，展现了顶尖互联网公司资深后端工程师的综合素养。其技术能力覆盖高并发、高可用系统架构设计、性能调优到线上SRE应急应对，均有深度实战案例和量化成果，明显高于同级别候选人。在团队合作与领导方面，具备推动跨团队共识、组织技术分享、带教新人的能力。面对压力场景和复杂业务瓶颈，展现出冷静应对、系统性规划与快速恢复能力。其对于技术文化、学习氛围和创新驱动力有实际倡议和推动，是一位兼具执行力与组织影响力的高级技术人才。\n\n---\n\n## 3. 详细法证式分析(Detailed Forensic Analysis) (阶段二分析(Second-Pass Analysis))\n\n**问题1（订单链路优化技术难题）**  \n候选人直接定位系统瓶颈（级联超时），提出“链路收敛优化+异步消息化改造”，引入自研链路追踪工具，有效量化指标提升（成功率从99.2%到99.8%，平均响应时间下降20%）。追问环节进一步展示运用动态线程池扩容与RocketMQ延迟机制，解决了实际生产环境新瓶颈。体现了出色的系统性、前瞻性思考与应急演进能力。【战略性思维：9，技术执行力：10】  \n\n**问题2（团队分歧协作处理）；问题8（团队赋能与文化）**  \n在存在重大技术分歧时，候选人不仅主动组织讨论、理清多方案优劣，而且推动最终民主决策，落地后协助细节收尾。进一步以“新人带教、推技术雷达、持续激励、季度成长项目”等实际举措促进团队成长，体现了高成熟度的团队赋能与知识沉淀能力。【团队领导力：8，文化契合：9】  \n\n**问题3/4/5（高并发促销与架构稳定性、高峰保障、技术细节突破）**  \n面对亿级流量和极端秒杀场景，候选人方案涵盖多级缓存、限流熔断、异步队列、灰度发布、热点隔离、动态扩容、全链路可观测性。技术细节包括“滑动窗口+预热打散、验证码与异步库存预占”，并能结合Prometheus+Grafana制定全栈可视化运维。GC与内存调优案例证明其攻坚能力和精细化管控水平。双11全链路重构项目6周高强度迭代，无重大事故，事后深度复盘产出标准文档，充分反映工程体系化思维和极强交付能力。【技术执行力：10，战略性思维：9】  \n\n**问题7（危机应急应对）**  \n候选人快速诊断（监控链路指标）、选择应急架构切换、限时10分钟恢复服务并能事后总结优化，体现极强的系统应变力与全局意识。【战略性思维：9】  \n\n---\n\n## 4. 供决策委员会讨论的关键要点(Key Discussion Points for the Decision Committee)\n\n- 候选人在高并发、高可用系统架构和性能优化上的技术深度是否能够持续胜任公司顶尖促销活动的架构稳定性要求？\n- 其团队主导力、实践中的知识传承方法论（如技术雷达分享与带新机制）是否适合本团队的实际氛围与成长目标？\n- 结合候选人极强的工程交付能力，后续用人部门如何为其设置更具挑战性的攻坚和演进任务，充分发挥候选人组织影响力与创新驱动力？\n\n---\n
    """
    interview_evaluation_score_content = {
        "resume_score": 85,
        "human_interviewer_score": 90,
        "interview_score": {
            "strategic_thinking": 9,
            "team_leadership": 8,
            "technical_execution": 10,
            "cultural_alignment": 9
        }
    }
    talents = [Talent(id="1", name="李磊", resume_content=resume1, score=86, resume_match_content=match1,
                      resume_match_score_content=match_score1,
                      interview_question_content=question1, interview_record_content=record1,
                      interview_evaluation_content=interview_evaluation_content,
                      interview_evaluation_score_content=interview_evaluation_score_content)]
    for talent in talents:
        await save_tenant(talent)

    # "talents": talents,
    return Command(
        update={
            "locale": "en-ZH",
            "agent_code": 'interview',
            "final_report": final_report,
            "jd_content": jd
        },
        goto="interview_evaluator_summarize"
    )


async def test_start_node_ogsm(
        state: State, config: RunnableConfig
) -> Command:
    """ogs 测试节点."""
    logger.info("test start node is running.")
    current_plan = Plan(
        has_enough_context=True,
        locale="zh-CN",
        title="密雪冰城 OGSMARTC 战略规划",
        thought="探索低成本扩张策略如何与 OGSM 架构对齐。",
        steps=[]
    )

    return Command(
        update={
            "locale": "en-ZH",
            "agent_code": "ogsm",
            "current_plan": current_plan,
        },
        goto="reporter"
    )


async def resume_matcher_loop_node(
        state: State, config: RunnableConfig
) -> Command[Literal["human_feedback2"]]:
    """简历匹配loop节点."""
    logger.info("Resume matcher node is running.")
    thread_id = config.get("configurable", {}).get("thread_id")
    talents = await get_tenants(thread_id)

    if len(talents) == 0:
        return Command(goto="__end__")

    for talent in talents:
        if talent.score is not None:
            talent.score = None
            talent.resume_match_content = None
            await save_tenant(talent)

    # resumes
    return Command(goto="resume_matcher")


async def resume_matcher_node(
        state: State, config: RunnableConfig
) -> Command[Literal["resume_matcher_loop", "human_feedback2"]]:
    """简历匹配节点."""
    thread_id = config.get("configurable", {}).get("thread_id")
    talents = await get_tenants(thread_id)

    current_talent = None

    for talent in talents:
        if talent.resume_match_score_content is None:
            current_talent = talent
            break

    # 完成所有匹配打分
    if current_talent is None:
        logger.info("Resume scoring node is finished.")
        return Command(goto="resume_matcher_summarize")

    logger.info("Resume scoring node is running...." + current_talent.name)
    input_ = {
        "messages": [
            HumanMessage(
                content=f"以下是岗位的能力分析矩阵：{state.get('final_report')}",
                name="resume_matcher",
            ),
            HumanMessage(
                content=f"下面是{current_talent.name}的简历信息：{current_talent.resume_content}",
                name="resume_matcher",
            )
        ],
        "locale": state.get("locale", "en-US"),
    }
    invoke_messages = apply_prompt_template("resume_matcher", input_, state['agent_code'], None)

    # 添加 feedback 消息
    messages = [msg for msg in state["messages"] if getattr(msg, "name", None) == "resume_matcher_loop"]
    invoke_messages.extend(messages)

    response = await get_llm_by_type(AGENT_LLM_MAP["default"]).ainvoke(invoke_messages)

    response_content = response.content
    result = json.loads(repair_json_output(response_content))
    # 保存一个chunk_message
    await save_message_chunk(result, response.id, state["user_id"], thread_id, "resume_matcher")

    current_talent.resume_match_content = result["content"]
    current_talent.resume_match_score_content = result["score"]
    await save_tenant(current_talent)

    return Command(goto="resume_matcher")


async def resume_matcher_summarize_node(
        state: State, config: RunnableConfig
) -> Command[Literal["human_feedback2"]]:
    thread_id = config.get("configurable", {}).get("thread_id")
    talents = await get_tenants(thread_id)

    input_ = {
        "messages": [
        ],
        "locale": state.get("locale", "en-US"),
    }

    invoke_messages = apply_prompt_template("resume_matcher_summarize", input_, state['agent_code'], None)

    for talent in talents:
        invoke_messages.append(HumanMessage(
            content=f"`{talent.name}`的简历评估,具体评估得分细节如下： {talent.resume_match_score_content}",
            name="resume_matcher_summarize",
        ))

    invoke_messages.append(SystemMessage(content="IMPORTANT: you need to summarize the matches of all resumes！！"))

    tools = [python_repl_tool]
    agent = create_agent("resume_matcher_summarize", "default", tools, "resume_matcher_summarize", state["agent_code"])

    full_response = await agent.ainvoke(
        input={"messages": invoke_messages},
        config={
            "recursion_limit": 10
        }
    )
    logger.info(f"resume matcher summarize result:{full_response}")

    content = full_response["messages"][-2].content

    # 保存content的chunk_message的消息
    await save_message_chunk(content, full_response["messages"][-2].id, state["user_id"], thread_id,
                             'resume_matcher_summarize')
    # 查找 "Stdout:" 并提取其后内容
    if "Stdout:" in content:
        stdout_result = content.split("Stdout:", 1)[1].strip()
        results = json.loads(stdout_result)
        for result in results:
            talent = next(
                (talent for talent in talents if talent.name == result.get("name")),
            )
            if talent is not None:
                talent.score = result.get("final_score")
                #  生成文件
                markdown_pre = f"""
                ## **{talent.name} {talent.score}分**
                #### **年龄：{talent.age} 性别：{talent.gender} 手机号：{talent.mobile} 学历：{talent.education} 工作年限：{talent.experience}**
                #
                # """
                thread_id = config.get("configurable", {}).get("thread_id")
                file_name = f'{thread_id}_{talent.name}_match.pdf'
                talent.resume_match_url = gen_pdf_upload(markdown_pre, talent.resume_match_content, file_name)
                await save_tenant(talent)

    return Command(
        update={
            "feedback_accept_node": "question_generator_loop",
            "feedback_edit_node": "resume_matcher_loop"
        },
        goto="human_feedback2",
    )


async def question_generator_loop_node(
        state: State, config: RunnableConfig
) -> Command[Literal["human_feedback2"]]:
    """面试问题生成节点."""
    logger.info("question generator node is running.")

    thread_id = config.get("configurable", {}).get("thread_id")
    talents = await get_tenants(thread_id)

    if talents is None or len(talents) == 0:
        return Command(goto="__end__")

    for talent in talents:
        if talent.interview_question_content is not None:
            talent.interview_question_content = None
            talent.interview_question_url = None
            await save_tenant(talent)

    return Command(goto="question_generator")


async def question_generator_node(
        state: State, config: RunnableConfig
) -> Command[Literal["human_feedback2"]]:
    """面试问题生成节点."""

    thread_id = config.get("configurable", {}).get("thread_id")
    talents = await get_tenants(thread_id)

    current_talent = None

    for talent in talents:
        if talent.interview_question_content is None:
            current_talent = talent
            break

    # 完成所有匹配打分
    if current_talent is None:
        logger.info("Question generator node is finished.")
        return Command(
            update={
                "feedback_accept_node": "interview_evaluator_loop",
                "feedback_edit_node": "question_generator_loop",
            },
            goto="human_feedback2",
        )

    logger.info("Question generator node is running！" + current_talent.name)

    input_ = {
        "messages": [
            HumanMessage(
                content="IMPORTANT: you need answer the question in chinese",
                name="system",
            ),
            HumanMessage(
                content=f"以下是岗位的能力分析矩阵信息：{state.get('final_report')}",
                name="resume_matcher",
            ),
            HumanMessage(
                f"# 下面是 {current_talent.name} 的简历信息 :{current_talent.resume_content}"
            ),
            HumanMessage(
                f"# 下面是 {current_talent.name} 的简历评估报告内容 :{current_talent.resume_match_content}"
            )
        ],
        "locale": state.get("locale", "en-ZH"),
    }
    invoke_messages = apply_prompt_template("question_generator", input_, state['agent_code'], None)

    # 添加 feedback 消息
    messages = [msg for msg in state["messages"] if getattr(msg, "name", None) == "question_generator_loop"]
    invoke_messages.extend(messages)

    response = await get_llm_by_type(AGENT_LLM_MAP["default"]).ainvoke(invoke_messages)

    logger.info(f"response: {response.content}")
    result = json.loads(repair_json_output(response.content))

    # 保存一个chunk_message
    await save_message_chunk(result, response.id, state["user_id"], thread_id, "question_generator")

    current_talent.interview_question_content = result["content"]

    #  生成文件
    markdown_pre = f"""
        ## **{current_talent.name} {current_talent.score}分**
        #### **年龄：{current_talent.age} 性别：{current_talent.gender} 手机号：{current_talent.mobile} 学历：{current_talent.education} 工作年限：{current_talent.experience}**

        """
    thread_id = config.get("configurable", {}).get("thread_id")
    file_name = f'{thread_id}_{current_talent.name}_question.pdf'
    current_talent.interview_question_url = gen_pdf_upload(markdown_pre, current_talent.interview_question_content,
                                                           file_name)
    await save_tenant(current_talent)

    return Command(goto="question_generator")


async def interview_evaluator_loop_node(
        state: State, config: RunnableConfig
) -> Command[Literal["human_feedback2"]]:
    """面试评估loop节点."""
    logger.info("Start interview evaluation.")
    thread_id = config.get("configurable", {}).get("thread_id")

    talents = await get_tenants(thread_id)
    if len(talents) == 0:
        return Command(goto="__end__")

    for talent in talents:
        if talent.interview_evaluation_content is not None:
            talent.interview_evaluation_content = None
            talent.interview_evaluation_url = None
            talent.interview_evaluation_score_content = None
            await save_tenant(talent)

    # resumes
    return Command(goto="interview_evaluator")


async def interview_evaluator_node(
        state: State, config: RunnableConfig
) -> Command[Literal["interview_evaluator_summarize"]]:
    """面试评估报告节点."""
    thread_id = config.get("configurable", {}).get("thread_id")
    talents = await get_tenants(thread_id)

    current_talent = None

    for talent in talents:
        if talent.interview_evaluation_content is None:
            current_talent = talent
            break

    # 完成所有简历的面试评估
    if current_talent is None:
        logger.info("Interview evaluator_node node is finished.")
        return Command(goto="interview_evaluator_summarize")

    logger.info("Interview evaluator node is running！" + current_talent.name)
    input_ = {
        "messages": [
            HumanMessage(
                content=f"以下是岗位的能力分析矩阵信息：{state.get('final_report')}",
                name="resume_matcher",
            ),
            HumanMessage(
                f"# 下面是 {current_talent.name} 的简历信息 :{current_talent.resume_content}"
            ),
            HumanMessage(
                f"# 下面是 {current_talent.name} 的简历评估报告信息,简历最终得分：{current_talent.score}分，具体得分明细如下 :{current_talent.resume_match_content}"
            ),
            HumanMessage(
                f"# 下面是 {current_talent.name} 的面试记录信息 :{current_talent.interview_record_content}"
            )
        ],
        "locale": state.get("locale", "en-ZH"),
    }
    invoke_messages = apply_prompt_template("interview_evaluator", input_, state['agent_code'], None)

    # 添加 feedback 消息
    messages = [msg for msg in state["messages"] if getattr(msg, "name", None) == "interview_evaluator_loop"]
    invoke_messages.extend(messages)

    response = await get_llm_by_type(AGENT_LLM_MAP["default"]).ainvoke(invoke_messages)

    logger.info(f"interview evaluator result:{response.content}")
    result = json.loads(repair_json_output(response.content))

    # 保存一个chunk_message
    thread_id = config.get("configurable", {}).get("thread_id")
    await save_message_chunk(result, response.id, state["user_id"], thread_id, "interview_evaluator")

    current_talent.interview_evaluation_content = result["content"]
    current_talent.interview_evaluation_score_content = result["score"]
    await save_tenant(current_talent)

    return Command(goto="interview_evaluator")


async def interview_evaluator_summarize_node(
        state: State, config: RunnableConfig
) -> Command[Literal["human_feedback2"]]:
    thread_id = config.get("configurable", {}).get("thread_id")
    talents = await get_tenants(thread_id)
    invoke_messages = []

    for talent in talents:
        invoke_messages.append(HumanMessage(
            content=f"`{talent.name}`的面试评估得分细则如下： {talent.interview_evaluation_score_content}",
            name="interview_evaluator_summarize",
        ))

    invoke_messages.append(SystemMessage(content="IMPORTANT: you need to summarize the evaluator of all interviews！！"))

    tools = [python_repl_tool]

    agent = create_agent("interview_evaluator_summarize", "default", tools, "interview_evaluator_summarize",
                         state["agent_code"])

    full_response = await agent.ainvoke(
        input={"messages": invoke_messages},
        config={
            "recursion_limit": 10
        })
    logger.info(f"interview evaluator summarize result:{full_response}")

    content = full_response["messages"][-2].content

    # 保存一个chunk_message
    await save_message_chunk(content, full_response["messages"][-2].id, state["user_id"], thread_id,
                             "interview_evaluator_summarize")

    # 查找 "Stdout:" 并提取其后内容
    if "Stdout:" in content:
        stdout_result = content.split("Stdout:", 1)[1].strip()
        results = json.loads(stdout_result)
        for result in results:
            talent = next(
                (talent for talent in talents if talent.name == result.get("name")),
            )
            if talent is not None:
                talent.interview_score = result.get("ai_score")
                # 生成文件
                markdown_pre = f"""##*{talent.name} {result.get("composite_score")}分**
                         #### **年龄：{talent.age} 性别：{talent.gender} 手机号：{talent.mobile} 学历：{talent.education} 工作年限：{talent.experience}**
                         """
                thread_id = config.get("configurable", {}).get("thread_id")
                file_name = f'{thread_id}_{talent.name}_evaluation.pdf'
                talent.interview_evaluation_url = gen_pdf_upload(markdown_pre, talent.interview_evaluation_content,
                                                                 file_name)
                await save_tenant(talent)

    return Command(
        update={
            "feedback_accept_node": "__end__",
            "feedback_edit_node": "interview_evaluator_loop"
        },
        goto="human_feedback2",
    )


async def human_feedback2_node(
        state, config: RunnableConfig
) -> Command[Literal[
    "jd_generator", "test_start", "resume_matcher_loop", "question_generator_loop", "interview_evaluator_loop", "__end__"]]:

    feedback = interrupt("{}")

    if feedback and str(feedback).upper().startswith("[EDIT]"):
        return Command(
            update={
                "messages": [
                    HumanMessage(content=feedback[6:], name=state.get("feedback_edit_node"))
                ],
            },
            goto=state.get("feedback_edit_node")
        )

    elif feedback and str(feedback).upper().startswith("[ACCEPTED]"):
        logger.info("Process is accepted by user.")
        return Command(goto=state.get("feedback_accept_node"))
    else:
        raise TypeError(f"Interrupt value of {feedback} is not supported.")
